/**
 * 
 */
package com.orcwar.gui.animations;

import static com.orcwar.engine.OWConstants.UNIT_MOVEMENT_SPEED;

import java.util.List;

import com.orcwar.engine.OWTile;
import com.orcwar.engine.OWUnit;
import com.orcwar.gui.OWGUI;

/**
 * Handle the movement of a unit through a list of tiles.
 * @author Callil
 *
 */
public class OWUnitMover {
	
	/** The game. */
	private OWGUI gui;
	
	/** The unit to move. */
	private OWUnit unit;
	
	/** The initial tile of the unit (where the movement begins).
	 * Not used so far, but might be used later on. */
	private OWTile initialFromTile;
	
	/** The list of tile representing the path of the unit, the last
	 * one being the real destination. */
	private List<OWTile> destinationTiles;
	
	/** Whether the movement is over. */
	private boolean done = false;
	
	//Process attributes
	/** The current from tile. */
	private OWTile currentFromTile;
	/** The current destination. */
	private OWTile currentDestination;
	/** Whether the unit is transiting between two tiles.*/
	private boolean isBetweenTile = false;

	
	/**
	 * Create a new OWUnitMover to handle the smooth movement of a unit through
	 * a list of (contiguous) tiles.
	 * @param unit the unit to move
	 * @param fromTile the tile the unit is standing before begining to move
	 * @param the list of tile to move across
	 */
	public OWUnitMover(OWGUI gui, OWUnit unit, OWTile fromTile, List<OWTile> destinations) {
		this.gui = gui;
		this.unit = unit;
		this.initialFromTile = fromTile;
		this.destinationTiles = destinations;
		
		currentFromTile = fromTile;
		if (destinations != null && destinations.size() > 0) {
			currentDestination = destinations.get(0);
		}
		
		unit.setOrientation(currentFromTile.directionToTile(currentDestination));
		
		//Disable UI actions during the movement
		gui.setActionsBlocked(true);
		//Hide the highlight of tiles
		gui.clearHighlights();
		//Unselect the unit
		gui.getController().deselectUnit();
	}

	
	/**
	 * Update.
	 * @param gameDuration the game duration
	 */
	public void update(long gameDuration) {
		
		if (finalDestinationReached()) {
			//End of the movement
			finish();
		} else {
			
			if (!isBetweenTile) {
				//Change the orientation of the unit
				unit.setOrientation(currentFromTile.directionToTile(currentDestination));
			}
			
			if (unit.getPosition().left == currentDestination.getPosition().left
					&& unit.getPosition().top == currentDestination.getPosition().top) {
				//arrived on the currentDestination
				isBetweenTile = false;
				destinationTiles.remove(0);
				currentFromTile = currentDestination;
				
				if(destinationTiles.size() > 0) {
					currentDestination = destinationTiles.get(0);
				}
			} else {
				//Still oving toward the current destination
				float remainingXTilesToGo = Math.abs(unit.getPosition().left - currentDestination.getPosition().left);
				float remainingYTilesToGo = Math.abs(unit.getPosition().top - currentDestination.getPosition().top);
				
				float xSign = Math.signum(currentDestination.getPosition().left - unit.getPosition().left);
				float ySign = Math.signum(currentDestination.getPosition().top - unit.getPosition().top);
				
				float xMove = remainingXTilesToGo > UNIT_MOVEMENT_SPEED ? UNIT_MOVEMENT_SPEED : remainingXTilesToGo;
				float yMove = remainingYTilesToGo > UNIT_MOVEMENT_SPEED ? UNIT_MOVEMENT_SPEED : remainingYTilesToGo;
				
				unit.getPosition().offset(xSign * xMove, ySign * yMove);
			}
			
			
		}
		
		
	}
	
	
	
	/**
	 * Finish the movement.
	 */
	private void finish() {
		//Set the mover as done so that the game will remove it at the next
		//update
		done = true;

		//re enable the UI actions
		gui.setActionsBlocked(false);
	}
	
	
	/**
	 * Check whether the unit has reached it's final destination or not.
	 * @return true if the destination has been reached
	 */
	private boolean finalDestinationReached() {
		return destinationTiles == null || destinationTiles.size() == 0;
	}


	/**
	 * @return the done
	 */
	public boolean isDone() {
		return done;
	}
	
	/** @return The unit. */
	public OWUnit getUnit() {
		return unit;
	}
}
